﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Pixysoft.Framework.Reflection;
using System.Xml.Serialization;
using System.Reflection;
using Pixysoft.Framework.Verifications;

namespace Pixysoft.Framework.Noebe
{
    class EntityHelper
    {
        public static DataTable EntityToDataTable(params IEntityBase[] entities)
        {
            DataTable table = null;

            if (entities == null)
                return null;

            if (entities.Length == 0)
                return null;

            foreach (IEntityBase entity in entities)
            {
                if (table == null)
                {
                    table = CreateDataTableSchema(entity.GetType());
                }

                IBeanMap map = ReflectionManager.CreateBeanMap(entity);

                DataRow row = table.NewRow();

                foreach (string propertyKey in map.Keys)
                {
                    if (!map.HasValue(propertyKey))
                        continue;

                    string columnName = GetDatabaseName(propertyKey);

                    if (!table.Columns.Contains(columnName))
                        continue;

                    row[columnName] = map.GetValue(propertyKey);
                }

                table.Rows.Add(row);
            }

            return table;
        }

        private static DataTable CreateDataTableSchema(Type objectType)
        {
            string tableName = null;

            object[] roots = GetCustomAttributes(objectType);

            if (roots == null)
                tableName = GetDatabaseName(objectType.Name);
            else
                tableName = GetDatabaseName((roots[0] as XmlRootAttribute).ElementName);

            DataTable tb = new DataTable(tableName);

            foreach (PropertyInfo property in objectType.GetProperties())
            {
                string columnName = GetDatabaseName(property.Name);

                if (tb.Columns.Contains(columnName))
                    continue;

                tb.Columns.Add(columnName, property.PropertyType);
            }

            return tb;
        }

        private static object[] GetCustomAttributes(Type objectType)
        {
            object[] roots = objectType.GetCustomAttributes(typeof(XmlRootAttribute), true);

            if (roots != null && roots.Length > 0)
                return roots;

            foreach (Type itype in objectType.GetInterfaces())
            {
                roots = GetCustomAttributes(itype);

                if (roots != null)
                    return roots;
            }

            return null;
        }


        public static List<T> DataTableToEntity<T>(DataTable table)
        {
            List<T> entities = new List<T>();

            Type objectType = typeof(T);

            foreach (DataRow row in table.Rows)
            {
                T entity = DataTableToEntity<T>(objectType, row);

                entities.Add(entity);
            }

            return entities;
        }

        public static T DataTableToEntity<T>(DataRow row)
        {
            Type objectType = typeof(T);

            return DataTableToEntity<T>(objectType, row);
        }

        private static T DataTableToEntity<T>(Type objectType, DataRow row)
        {
            object entity = null;

            if (objectType.IsInterface)
            {
                entity = ReflectionManager.CreatePojo<T>();
            }
            else
            {
                entity = objectType.GetConstructor(Type.EmptyTypes).Invoke(null);
            }

            IBeanMap beanMap = ReflectionManager.CreateBeanMap(entity);

            foreach (string propertyKey in beanMap.Keys)
            {
                string columnName = GetDatabaseName(propertyKey);

                if (!row.Table.Columns.Contains(columnName))
                    continue;

                object propertyValue = row[columnName];

                beanMap.SetValue(propertyKey, propertyValue);
            }

            return beanMap.GetBean<T>();
        }

        private static PropertyInfo GetProperty(Type objectType, string propertyKey)
        {
            foreach (PropertyInfo info in objectType.GetProperties())
            {
                if (string.Equals(info.Name, propertyKey, StringComparison.OrdinalIgnoreCase))
                    return info;
            }

            throw Exceptions.UnexpectedResultException("missing property in object. property:{0}. type:{1}", propertyKey, objectType.Name);
        }

        /// <summary>
        /// 表结构名转化为对象名
        /// </summary>
        /// <param name="databaseSchemaName"></param>
        /// <returns></returns>
        public static string getEntityClassName(string databaseSchemaName)
        {
            StringBuilder builder = new StringBuilder();

            for (int index = 0; index < databaseSchemaName.Length; index++)
            {
                char tableChar = databaseSchemaName[index];

                if (index == 0)
                {
                    builder.Append(Char.ToUpper(tableChar));

                    continue;
                }

                if (tableChar == '_')
                {
                    builder.Append(Char.ToLower(tableChar));
                    index++;
                    tableChar = databaseSchemaName[index];
                    builder.Append(Char.ToUpper(tableChar));
                }
                else
                {
                    builder.Append(Char.ToLower(tableChar));
                }
            }

            return builder.ToString();
        }

        /// <summary>
        /// 表结构字段 转化为属性
        /// </summary>
        /// <param name="databaseSchemaName"></param>
        /// <returns></returns>
        public static String getEntityPropertyName(String databaseSchemaName)
        {
            StringBuilder builder = new StringBuilder();

            for (int index = 0; index < databaseSchemaName.Length; index++)
            {
                char tableChar = databaseSchemaName[index];

                if (index == 0)
                {
                    builder.Append(Char.ToLower(tableChar));

                    continue;
                }

                if (tableChar == '_')
                {
                    builder.Append(Char.ToLower(tableChar));
                    index++;
                    tableChar = databaseSchemaName[index];
                    builder.Append(Char.ToUpper(tableChar));
                }
                else
                {
                    builder.Append(Char.ToLower(tableChar));
                }
            }

            return builder.ToString();
        }


        private static string GetDatabaseName(string entityName)
        {
            return entityName.Trim().ToUpper();
        }

        internal static string GetTableName(Type type)
        {
            object[] roots = type.GetCustomAttributes(typeof(XmlRootAttribute), true);

            if (roots == null || roots.Length == 0)
                return GetDatabaseName(type.Name);
            else
                return GetDatabaseName((roots[0] as XmlRootAttribute).ElementName);
        }
    }
}
